package org.csc.sss.api.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.csc.sss.api.controller.base.BaseController;
import org.csc.sss.core.annontation.WebLog;
import org.csc.sss.util.RequestUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class WebLogAspect extends BaseController {

    ThreadLocal<Map<Integer, Object>> mapThreadLocal = new ThreadLocal<>();

    @Pointcut("execution(public * org.csc.sss.api.controller.base.*.*(..))")
    public void webLog(){}

    @Around("webLog()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        // 这里执行保存日志的动作
        Long startTime = System.currentTimeMillis();
        Object target = joinPoint.getTarget();
        Class<?> cls = getTargetClass(target);
        Method specificMethod = getSpecificMethod(joinPoint, cls);
        WebLog webLog = specificMethod.getAnnotation(WebLog.class);
        //获得切面业务名称
        String logger = "日志信息不存在";
        if (null != webLog) {
            logger = webLog.value();
        }

        //获取当前用户
//        String userStr = super.getUser().getId();
        String userStr = "cs";
        userStr = userStr == null ? "~public" : userStr;

        //获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        Map<Integer, Object> map = new HashMap<>();
        String key = RequestUtils.getParamMap(request).get("data");
        int hashCode = key.hashCode();
        log.info("hashCode -- ---------> {}", hashCode);
        Map<Integer, Object> objectMap = mapThreadLocal.get();
        if (null != objectMap) {
            Object value = objectMap.get(hashCode);
            log.info("value -- ---------> {}", value);
            if (value != null) {
                return value;
            }
        }

        // 该日志必不可少
        request.setCharacterEncoding("UTF-8");
        String requestBody = "";
        try {
//            requestBody = IOUtil.readString(request.getInputStream(), request.getContentLength());
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("requestBody exception", e);
            }
        }
        log.info("用户[{}]开始业务[{}]-参数:{}-requestBody:{}", userStr, logger, RequestUtils.getParamMap(request),requestBody);

        Object ret = joinPoint.proceed(); //返回结果
        log.info("ret -- ---------> {}", ret);
        map.put(hashCode, ret);
        mapThreadLocal.set(map);
        Long endTime = System.currentTimeMillis();
        log.info("用户[{}]执行业务[{}]完成-返回:{}-本次请求处理时间为: {}ms", userStr, logger, "", new Long(endTime - startTime));

        return ret;
    }

    /**
     * 获取目标Class
     *
     * @param target
     * @return
     */
    private Class<?> getTargetClass(Object target) {
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
        if (targetClass == null) {
            targetClass = target.getClass();
        }
        return targetClass;
    }

    /**
     * 获取指定方法
     *
     * @param pjp
     * @param targetClass
     * @return
     */
    private Method getSpecificMethod(ProceedingJoinPoint pjp, Class<?> targetClass) {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        return specificMethod;
    }

    }

